/*
 * Copyright 2008-2023 dexian.vip. All rights reserved.
 * Support: http://www.dexian.vip
 * License: http://www.dexian.vip/license
 */

package vip.dexian.admin.service.impl;

import vip.dexian.admin.assembler.RoleCommandAssembler;
import vip.dexian.admin.command.RoleSaveCommand;
import vip.dexian.admin.command.RoleUpdateCommand;
import vip.dexian.admin.dao.RoleDao;
import vip.dexian.admin.dto.RoleDTO;
import vip.dexian.admin.entity.Admin;
import vip.dexian.admin.entity.Role;
import vip.dexian.admin.entity.RoleDepartment;
import vip.dexian.admin.enums.DataScopeEnum;
import vip.dexian.admin.service.OnlineAdminService;
import vip.dexian.admin.service.RoleDepartmentService;
import vip.dexian.admin.service.RoleService;
import vip.dexian.common.CommonAttributes;
import vip.dexian.common.query.Filter;
import vip.dexian.common.utils.CollectionUtils;
import vip.dexian.common.utils.FilterUtils;
import vip.dexian.common.utils.LocalDateTimeUtils;
import vip.dexian.core.base.service.BaseServiceImpl;
import vip.dexian.core.base.validation.group.Save;
import vip.dexian.core.base.validation.group.Update;
import vip.dexian.core.config.redis.RedisCacheKeyAttributes;
import vip.dexian.core.exception.ServiceValidationException;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Set;

/**
 * 角色业务逻辑处理实现
 *
 * @author 挺好的 2023年06月02日 08:45:40
 */
@Service ("roleServiceImpl")
@Slf4j
public class RoleServiceImpl extends BaseServiceImpl <Role, RoleDTO, Long> implements RoleService {

    /**
     * 角色Dao
     */
    @Resource (name = "roleDaoImpl")
    private RoleDao roleDao;

    /**
     * command -> entity
     */
    @Resource (name = "roleCommandAssemblerImpl")
    private RoleCommandAssembler roleCommandAssembler;

    /**
     * 在线管理员管理
     */
    @Resource (name = "onlineAdminServiceImpl")
    private OnlineAdminService onlineAdminService;

    /**
     * 角色 -> 部门关联
     */
    @Resource (name = "roleDepartmentServiceImpl")
    private RoleDepartmentService roleDepartmentService;

    /**
     * 根据id查找角色
     *
     * @param id
     *         ID
     *
     * @return {@link vip.dexian.admin.dto.RoleDTO}
     */
    @Override
    @Transactional (readOnly = true, propagation = Propagation.SUPPORTS)
    public RoleDTO find (Long id) {
        RoleDTO roleDTO = super.find(id);

        if (roleDTO != null && DataScopeEnum.CUSTOM.equals(roleDTO.getDataScope())) {
            // 查找该角色关联的部门
            roleDTO.setRoleDepartments(this.roleDepartmentService.findByRoleId(id));
        }

        return roleDTO;
    }

    /**
     * 保存角色信息
     *
     * @param command
     *         保存指令
     *
     * @return {@link Role}
     */
    @Override
    @Transactional (rollbackFor = Exception.class)
    public Role save (RoleSaveCommand command) {
        log.info("save command: {}", command);
        Role role = this.roleCommandAssembler.toSave(command, Save.class);

        if (this.doIdentityExists(role.getIdentity())) {
            throw new ServiceValidationException("A20001");
        }

        DataScopeEnum dataScope = command.getDataScope();

        if (!DataScopeEnum.CUSTOM.equals(dataScope)) {
            return super.save(role);
        }

        this.addRoleDepartment(command.getDepartmentIds(), role);

        return super.save(role);
    }

    /**
     * 修改角色信息
     *
     * @param command
     *         修改指令
     *
     * @return {@link Role}
     */
    @Override
    @Transactional (rollbackFor = Exception.class)
    @CacheEvict (value = RedisCacheKeyAttributes.ROLE_DEPARTMENT_KEY, allEntries = true)
    public Role update (RoleUpdateCommand command) {
        log.info("update command: {}", command);
        Role role = this.roleCommandAssembler.toUpdate(command, Update.class);

        Long id = command.getId();

        Role dbRole = this.roleDao.findByValidationIfNull(id, "角色");

        // 角色标识有变更
        if (!StringUtils.equals(command.getFullIdentity(), dbRole.getIdentity()) && this.doIdentityExists(
                command.getIdentity())) {
            throw new ServiceValidationException("A20001");
        }

        DataScopeEnum dataScope = role.getDataScope();

        // 清空原来绑定的部门
        this.roleDepartmentService.deleteByRoleId(id);

        // 如果是自定义的， 重新保存部门数据
        if (DataScopeEnum.CUSTOM.equals(dataScope)) {
            this.addRoleDepartment(command.getDepartmentIds(), dbRole);
        }

        // 踢出当前角色下的全部管理员
        this.onlineAdminService.logoutByRole(dbRole.getIdentity());

        return super.update(role);
    }


    /**
     * 根据id批量删除角色
     *
     * @param ids
     *         id列表
     */
    @Override
    @Transactional (rollbackFor = Exception.class)
    @CacheEvict (value = RedisCacheKeyAttributes.ROLE_DEPARTMENT_KEY, allEntries = true)
    public void delete (Long... ids) {
        log.info("delete parameter: {}", ids);
        this.validDelete(ids);
        List <Long> distinctIds = CollectionUtils.distinct(ids);

        for (Long id : distinctIds) {
            Role role = this.roleDao.findByValidationIfNull(id, "角色");

            if (Boolean.TRUE.equals(role.getIsSystem())) {
                throw new ServiceValidationException("A20002", role.getName());
            }

            // 查看角色下面是否有管理员。。
            Set <Admin> admins = role.getAdmins();

            if (org.apache.commons.collections4.CollectionUtils.isNotEmpty(admins)) {
                throw new ServiceValidationException("A20003", role.getName());
            }

            role.setUniqueTimestamp(LocalDateTimeUtils.nowToTimestamp());

            super.delete(role);

            // 清空原来绑定的部门
            this.roleDepartmentService.deleteByRoleId(id);

            // 踢出当前角色下的全部管理员
            this.onlineAdminService.logoutByRole(role.getIdentity());
        }

    }

    /**
     * 角色标识是否存在
     *
     * @param identity
     *         角色标识符
     *
     * @return 如果是保留标识、角色存在返回true
     */
    @Override
    @Transactional (propagation = Propagation.SUPPORTS, readOnly = true)
    public boolean isIdentityExists (String identity) {
        return this.doIdentityExists(identity);
    }

    /**
     * 角色标识是否存在
     *
     * @param identity
     *         角色标识符
     *
     * @return 如果是保留标识、角色存在返回true
     */
    private boolean doIdentityExists (String identity) {
        if (StringUtils.isEmpty(identity)) {
            return true;
        }

        // 如果没有以 ROLE_开头，则自动添加
        if (!identity.startsWith(CommonAttributes.RoleSetting.ROLE_PREFIX)) {
            identity = CommonAttributes.RoleSetting.ROLE_PREFIX + identity;
        }

        if (ArrayUtils.contains(CommonAttributes.RoleSetting.KEEP_KEYWORDS, identity)) {
            return true;
        }

        List <Filter> filters = FilterUtils.builder().equals("identity", identity).uniqueKey().build();
        return this.roleDao.exists(filters);
    }

    /**
     * 添加关联部门
     *
     * @param departmentIds
     *         部门id
     * @param role
     *         角色
     */
    private void addRoleDepartment (List <Long> departmentIds, Role role) {

        List <RoleDepartment> roleDepartments = this.roleDepartmentService.create(departmentIds, role);

        if (org.apache.commons.collections4.CollectionUtils.isEmpty(roleDepartments)) {
            throw new ServiceValidationException("A20005");
        }

        this.roleDepartmentService.save(roleDepartments);
    }


}
